#include "Section_StaticW.h"

#include <iostream>

#include "Node_Base.h"

#include "Section_beam_BT.h"

#include "Element_plane62_BT.h"
#include "Element_plane82_BT.h"

Section_StaticW::Section_StaticW(std::vector<std::vector<double>>& Bo_point, std::vector<std::vector<std::vector<double>>>& Bi_point, const std::shared_ptr<Section_Base>& Section, const std::shared_ptr<Node_Base>& Node, const std::map<int, std::shared_ptr<Element_Base>>& S_Element)
{
	m_Bo_point = Bo_point;
	m_Bi_point = Bi_point;
	m_parameter = Section->parameter;
	m_coordc = Section->coord_c;
	m_Node = Node;
	mS_Element = S_Element;
	m_Node->m_Sw.resize(2);
	std::cout << m_Node->m_Coord[0] << " " << m_Node->m_Coord[1] << std::endl;
	for (direction = 0; direction < 2; direction++)
	{
		InsertNode();
	}
}

void Section_StaticW::InsertNode()
{
	cdt.clear();
	Polygon_2 outer;
	for (int i = 0; i < m_Bo_point.size(); i++)
	{
		outer.push_back(Point_2(m_Bo_point[i][0]-m_coordc[0], m_Bo_point[i][1] - m_coordc[1]));
	}
	Polygon_with_holes original(outer);

 	if (!m_Bi_point.empty())
	{
		std::vector<Polygon_2> Hole;
		Hole.resize(m_Bi_point.size());
		for (int i = 0; i < m_Bi_point.size(); i++)
		{
			for (int j = 0; j < m_Bi_point[i].size(); j++)
			{
				Hole[i].push_back(Point_2(m_Bi_point[i][j][0] - m_coordc[0], m_Bi_point[i][j][1] - m_coordc[1]));
				original.add_hole(Hole[i]);
			}
		}
	}

	Polygon_set original_set;
	original_set.insert(original);

	double coord2 = -1.0 * ((direction + 1) % 2);
	double coord1 = -1.0 * (direction % 2);
	Line_2 split_line(Point_2(m_Node->m_Coord[0], m_Node->m_Coord[1]), Point_2(m_Node->m_Coord[0] + coord1, m_Node->m_Coord[1] + coord2));

	double coord = -1 + 2 * ((direction + 1) % 2);
	Polygon_2 halfplane;
	halfplane.push_back(split_line.projection(Point_2(-1e6, -1e6 * coord)));
	halfplane.push_back(split_line.projection(Point_2(-1e6 * coord, 1e6)));
	halfplane.push_back(Point_2(-1e6 * coord, 1e6));
	halfplane.push_back(Point_2(-1e6, -1e6 * coord));
	Polygon_set halfplane_set;
	halfplane_set.insert(halfplane);

	Polygon_set left_part;
	left_part.intersection(original_set, halfplane_set);

	Polygon_set right_part;
	right_part.difference(original_set, halfplane_set);

	std::vector<Polygon_with_holes> leftpolygons;
	std::vector<Polygon_with_holes> rightpolygons;

	left_part.polygons_with_holes(std::back_inserter(leftpolygons));
	right_part.polygons_with_holes(std::back_inserter(rightpolygons));

	double leftarea = 0, rightarea = 0;
	for (int i = 0; i < leftpolygons.size(); i++)
	{
		leftarea += CGAL::to_double(CGAL::abs(leftpolygons[0].outer_boundary().area()));
		for (auto it = leftpolygons[i].holes_begin(); it != leftpolygons[i].holes_end(); it++)
		{
			leftarea -= CGAL::to_double(CGAL::abs(it->area()));
		}
	}
	for (int i = 0; i < rightpolygons.size(); i++)
	{
		rightarea += CGAL::to_double(CGAL::abs(rightpolygons[0].outer_boundary().area()));
		for (auto it = rightpolygons[i].holes_begin(); it != rightpolygons[i].holes_end(); it++)
		{
			rightarea -= CGAL::to_double(CGAL::abs(it->area()));
		}
	}

	std::cout << leftarea << " " << rightarea << std::endl;

	if (leftarea == 0 || rightarea == 0)
	{
		m_Node->m_Sw[direction] = 0;
	}
	else if (leftarea <= rightarea)
	{
		for (int i = 0; i < leftpolygons.size(); i++)
		{
			vh_outer.resize(leftpolygons[i].outer_boundary().size());
			int j = 0;
			for (auto vit = leftpolygons[i].outer_boundary().vertices_begin(); vit != leftpolygons[i].outer_boundary().vertices_end(); vit++)
			{
				vh_outer[j++] = cdt.insert(Point(CGAL::to_double(vit->x()), CGAL::to_double(vit->y())));
			}
			for (j = 0; j < vh_outer.size(); j++)
			{
				cdt.insert_constraint(vh_outer[j], vh_outer[(j + 1) % vh_outer.size()]);
			}
			if (!leftpolygons[i].holes().empty())
			{
				int nholes = 0;
				for (auto hit = leftpolygons[i].holes_begin(); hit != leftpolygons[i].holes_end(); hit++)
				{
					std::vector<Vertex_handle> holes;
					holes.resize(hit->size());
					j = 0;
					for (auto vit = hit->vertices_begin(); vit != hit->vertices_end(); vit++)
					{
						holes[j++] = cdt.insert(Point(CGAL::to_double(vit->x()), CGAL::to_double(vit->y())));
					}
					vh_inner.insert({ nholes++,holes });
					for (int i = 0; i < holes.size(); i++)
					{
						cdt.insert_constraint(holes[(i + 1) % holes.size()], holes[i]);
					}
				}
				hole.resize(nholes);
				for (j = 0; j < vh_inner.size(); j++)
				{
					std::vector<Point_2> inpoint;
					inpoint.resize(vh_inner.at(j).size());
					for (int ip = 0; ip < vh_inner.at(j).size(); ip++)
					{
						inpoint[ip] = Point_2(vh_inner.at(j)[ip]->point().x(), vh_inner.at(j)[ip]->point().y());
					}
					Line_2 line(inpoint[0], inpoint[inpoint.size() / 2]);
					if (inpoint.size() / 2 != 2)
					{
						auto pEntity = line.projection(inpoint[2]);
						double x1 = CGAL::to_double(pEntity.x() + inpoint[2].x()) / 2;
						double x2 = CGAL::to_double(pEntity.y() + inpoint[2].y()) / 2;
						hole[j++] = Point(x1, x2);
					}
					else
					{
						auto pEntity = line.projection(inpoint[1]);
						double x1 = CGAL::to_double(pEntity.x() + inpoint[1].x()) / 2;
						double x2 = CGAL::to_double(pEntity.y() + inpoint[1].y()) / 2;
						hole[j++] = Point(x1, x2);
					}
				}
			}
			MeshPart();
			AddNode();
			GetNodew();
		}
	}
	else if (leftarea > rightarea)
	{
		for (int i = 0; i < rightpolygons.size(); i++)
		{
			vh_outer.resize(rightpolygons[i].outer_boundary().size());
			int j = 0;
			for (auto vit = rightpolygons[i].outer_boundary().vertices_begin(); vit != rightpolygons[i].outer_boundary().vertices_end(); vit++)
			{
				vh_outer[j++] = cdt.insert(Point(CGAL::to_double(vit->x()), CGAL::to_double(vit->y())));
			}
			for (j = 0; j < vh_outer.size(); j++)
			{
				cdt.insert_constraint(vh_outer[j], vh_outer[(j + 1) % vh_outer.size()]);
			}
			if (!rightpolygons[i].holes().empty())
			{
				int nholes = 0;
				for (auto hit = rightpolygons[i].holes_begin(); hit != rightpolygons[i].holes_end(); hit++)
				{
					std::vector<Vertex_handle> holes;
					holes.resize(hit->size());
					j = 0;
					for (auto vit = hit->vertices_begin(); vit != hit->vertices_end(); vit++)
					{
						holes[j++] = cdt.insert(Point(CGAL::to_double(vit->x()), CGAL::to_double(vit->y())));
					}
					vh_inner.insert({ nholes++,holes });
					for (int i = 0; i < holes.size(); i++)
					{
						cdt.insert_constraint(holes[(i + 1) % holes.size()], holes[i]);
					}
				}
				hole.resize(nholes);
				for (j = 0; j < vh_inner.size(); j++)
				{
					std::vector<Point_2> inpoint;
					inpoint.resize(vh_inner.at(j).size());
					for (int ip = 0; ip < vh_inner.at(j).size(); ip++)
					{
						inpoint[ip] = Point_2(vh_inner.at(j)[ip]->point().x(), vh_inner.at(j)[ip]->point().y());
					}
					Line_2 line(inpoint[0], inpoint[inpoint.size() / 2]);
					if (inpoint.size() / 2 != 2)
					{
						auto pEntity = line.projection(inpoint[2]);
						double x1 = CGAL::to_double(pEntity.x() + inpoint[2].x()) / 2;
						double x2 = CGAL::to_double(pEntity.y() + inpoint[2].y()) / 2;
						hole[j++] = Point(x1, x2);
					}
					else
					{
						auto pEntity = line.projection(inpoint[1]);
						double x1 = CGAL::to_double(pEntity.x() + inpoint[1].x()) / 2;
						double x2 = CGAL::to_double(pEntity.y() + inpoint[1].y()) / 2;
						hole[j++] = Point(x1, x2);
					}
				}
			}
			MeshPart();
			AddNode();
			GetNodew();
		}
	}
}

void Section_StaticW::MeshPart()
{
	Mesher mesher(cdt);
	mesher.set_criteria(Criteria(0.125, m_parameter[0]));
	if (!hole.empty())
	{
		mesher.set_seeds(hole.begin(), hole.end(), false);
	}
	mesher.refine_mesh();
	std::cout << m_Node->m_id << std::endl;
	//CGAL::lloyd_optimize_mesh_2(cdt,
	//	CGAL::parameters::number_of_iterations(m_parameter[1]));
}

void Section_StaticW::AddNode()
{
	std::map<Vertex_handle, int> findNode;
	std::map<Vertex_handle, int> edgeNode;
	std::map<int, int> Add_Node;
	int idNode = 1, idElement = 1;

	for (int i = 0; i < vh_outer.size(); i++)
	{
		CDT::Segment pEntity(vh_outer[i]->point(), vh_outer[(i + 1) % vh_outer.size()]->point());
		seg_outer.insert({ i,pEntity });
	}

	for (auto& a : vh_inner)
	{
		auto& HOLENODES = a.second;
		for (int i = 0; i < HOLENODES.size(); i++)
		{
			CDT::Segment pEntity(HOLENODES[(i + 1) % HOLENODES.size()]->point(), HOLENODES[i]->point());
			seg_inner.insert({ i,pEntity });
		}
	}

	for (auto it = cdt.finite_vertices_begin(); it != cdt.finite_vertices_end(); it++)
	{
		auto pEntity = std::make_shared<Node_Base>();
		pEntity->m_id = idNode;
		pEntity->m_Coord.resize(2);
		pEntity->m_Coord[0] = it->point().x();
		pEntity->m_Coord[1] = it->point().y();
		edgeNode[it] = 1;

		for (auto& edge : seg_inner)
		{
			if (CGAL::squared_distance(it->point(), edge.second) < 1e-5)
			{
				edgeNode[it] = 0;
				break;
			}
		}

		findNode[it] = idNode;
		S_Node.insert({ idNode,pEntity });
		idNode++;
	}

	for (auto it = cdt.finite_faces_begin(); it != cdt.finite_faces_end(); it++)
	{
		auto pEntity = std::make_shared<Element_plane62_BT>();
		pEntity->m_pNode.resize(6);

		std::vector<int> n_Node;
		std::vector<int> Node_on_seg;
		n_Node.resize(3);
		Node_on_seg.resize(3);

		for (int i = 0; i < 3; i++)
		{
			n_Node[i] = findNode[it->vertex(i)];
			Node_on_seg[i] = edgeNode[it->vertex(i)];
		}
		int flag = n_Node[0] * n_Node[1] * n_Node[2] * (Node_on_seg[0] + Node_on_seg[1] + Node_on_seg[2]);

		if (it->is_in_domain() && flag)
		{
			pEntity->m_id = idElement;
			pEntity->m_pSection = mS_Element.at(1)->m_pSection;
			pEntity->m_pNode[0] = FindNode(findNode[it->vertex(0)]);
			pEntity->m_pNode[1] = FindNode(findNode[it->vertex(1)]);
			pEntity->m_pNode[2] = FindNode(findNode[it->vertex(2)]);

			std::vector<int> n0, n1, n2;
			n0.resize(2);
			n1.resize(2);
			n2.resize(2);
			n0[0] = findNode[it->vertex(1)];
			n0[1] = findNode[it->vertex(2)];
			n1[0] = findNode[it->vertex(0)];
			n1[1] = findNode[it->vertex(2)];
			n2[0] = findNode[it->vertex(0)];
			n2[1] = findNode[it->vertex(1)];

			pEntity->m_pNode[3] = Sorting(n0, Add_Node);
			pEntity->m_pNode[4] = Sorting(n1, Add_Node);
			pEntity->m_pNode[5] = Sorting(n2, Add_Node);

			S_Element.insert({ idElement,pEntity });
			idElement++;
		}
	}
}

void Section_StaticW::GetNodew()
{
	std::vector<std::vector<double>> edge;
	std::vector<double> Elementarea;
	edge.resize(mS_Element.size());
	Elementarea.resize(mS_Element.size());
	for (auto& b : mS_Element)
	{
		auto& pElement = b.second;
		int i = pElement->m_id - 1;
		edge[i].resize(6);
		edge[i][0] = pElement->m_pNode[0].lock()->m_Coord[0] - pElement->m_pNode[1].lock()->m_Coord[0];
		edge[i][1] = pElement->m_pNode[0].lock()->m_Coord[1] - pElement->m_pNode[1].lock()->m_Coord[1];
		edge[i][2] = pElement->m_pNode[1].lock()->m_Coord[0] - pElement->m_pNode[2].lock()->m_Coord[0];
		edge[i][3] = pElement->m_pNode[1].lock()->m_Coord[1] - pElement->m_pNode[2].lock()->m_Coord[1];
		edge[i][4] = pElement->m_pNode[2].lock()->m_Coord[0] - pElement->m_pNode[0].lock()->m_Coord[0];
		edge[i][5] = pElement->m_pNode[2].lock()->m_Coord[1] - pElement->m_pNode[0].lock()->m_Coord[1];
	}

	for (auto& a : S_Node)
	{
		auto& pNode = a.second;
		pNode->m_parameter.resize(3);
		for (int i = 0; i < edge.size(); i++)
		{
			int flag = 0;
			std::vector<double> Nodelink;
			double area1, area2;
			Nodelink.resize(6);
			Nodelink[0] = mS_Element.at(i + 1)->m_pNode[0].lock()->m_Coord[0] - pNode->m_Coord[0];
			Nodelink[1] = mS_Element.at(i + 1)->m_pNode[0].lock()->m_Coord[1] - pNode->m_Coord[1];
			Nodelink[2] = mS_Element.at(i + 1)->m_pNode[1].lock()->m_Coord[0] - pNode->m_Coord[0];
			Nodelink[3] = mS_Element.at(i + 1)->m_pNode[1].lock()->m_Coord[1] - pNode->m_Coord[1];
			Nodelink[4] = mS_Element.at(i + 1)->m_pNode[2].lock()->m_Coord[0] - pNode->m_Coord[0];
			Nodelink[5] = mS_Element.at(i + 1)->m_pNode[2].lock()->m_Coord[1] - pNode->m_Coord[1];
			if ((Nodelink[0] * edge[i][1] - Nodelink[1] * edge[i][0]) < 0)
			{
				if ((Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) < 0)
				{
					if ((Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) < 0)
					{
						area1 = abs(Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) / abs(edge[i][0] * edge[i][3] - edge[i][1] * edge[i][2]);
						area2 = abs(Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) / abs(edge[i][0] * edge[i][3] - edge[i][1] * edge[i][2]);
						flag = 1;
					}
				}
			}
			else if ((Nodelink[0] * edge[i][1] - Nodelink[1] * edge[i][0]) > 0)
			{
				if ((Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) > 0)
				{
					if ((Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) > 0)
					{
						area1 = abs(Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) / abs(edge[i][0] * edge[i][3] - edge[i][1] * edge[i][2]);
						area2 = abs(Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) / abs(edge[i][0] * edge[i][3] - edge[i][1] * edge[i][2]);
						flag = 1;
					}
				}
			}
			else if (abs(Nodelink[0] * edge[i][1] - Nodelink[1] * edge[i][0]) ==0)
			{
				if ((Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) * (Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) > 0)
				{
					area1 = abs(Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) / abs(edge[i][0] * edge[i][3] - edge[i][1] * edge[i][2]);
					area2 = abs(Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) / abs(edge[i][0] * edge[i][3] - edge[i][1] * edge[i][2]);
					flag = 1;
				}
			}
			else if (abs(Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) < 0)
			{
				if ((Nodelink[0] * edge[i][1] - Nodelink[1] * edge[i][0]) * (Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) > 0)
				{
					area1 = 0;
					area2 = abs(Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) / abs(edge[i][0] * edge[i][3] - edge[i][1] * edge[i][2]);
					flag = 1;
				}
			}
			else if (abs(Nodelink[4] * edge[i][5] - Nodelink[5] * edge[i][4]) < 0)
			{
				if ((Nodelink[0] * edge[i][1] - Nodelink[1] * edge[i][0]) * (Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) > 0)
				{
					area1 = abs(Nodelink[2] * edge[i][3] - Nodelink[3] * edge[i][2]) / abs(edge[i][0] * edge[i][3] - edge[i][1] * edge[i][2]);
					area2 = 0;
					flag = 1;
				}
			}
			if (flag == 1)
			{
				auto pEntity = std::dynamic_pointer_cast<Element_plane62_BT>(mS_Element.at(i + 1));
				pNode->m_parameter[2] = pEntity->get_w(area1, area2);
				break;
			}
		}
	}

	for (auto& a : S_Element)
	{
		auto pEntity = std::dynamic_pointer_cast<Element_plane62_BT>(a.second);
		pEntity->setElement();
		pEntity->get_Sw(m_Node->m_Sw[direction]);
	}
}

std::shared_ptr<Node_Base> Section_StaticW::FindNode(int idEntity)
{
	auto iFind = S_Node.find(idEntity);
	if (iFind != S_Node.end())
	{
		return iFind->second;
	}
	return nullptr;
}

std::shared_ptr<Node_Base> Section_StaticW::Sorting(std::vector<int>& n, std::map<int, int>& A_Node)
{
	int inter;
	if (n[0] > n[1])
	{
		inter = n[1];
		n[1] = n[0];
		n[0] = inter;
	}
	int z = 0.5 * (n[0] + n[1]) * (n[0] + n[1] + 1) + n[1];

	auto N0 = A_Node.find(z);
	if (N0 == A_Node.end())
	{
		A_Node[z] = S_Node.size() + 1;
		std::weak_ptr<Node_Base> pNode0 = FindNode(n[0]);
		std::weak_ptr<Node_Base> pNode1 = FindNode(n[1]);
		auto pEntity = std::make_shared<Node_Base>();
		pEntity->m_id = S_Node.size() + 1;
		pEntity->m_Coord.resize(2);
		pEntity->m_Coord[0] = (pNode0.lock()->m_Coord[0] + pNode1.lock()->m_Coord[0]) / 2;
		pEntity->m_Coord[1] = (pNode0.lock()->m_Coord[1] + pNode1.lock()->m_Coord[1]) / 2;
		S_Node.insert({ S_Node.size() + 1,pEntity });
	}
	return S_Node[A_Node[z]];
}
